using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using UnityEngine;
using Random = System.Random;

namespace Gj
{
    public class AttrEnv : ClearObject
    {
        internal static ConcurrentQueue<AttrEnv> queue = new();

        public ClearObject co;
        public AttrObject first;
        public AttrEffectList firstEffect;
        public float power;

        public Random random;

        // 整个周期一个，用于最后返回到外部
        public object result;
        public AttrObject second;
        public AttrEffectList secondEffect;

        public int time;

        protected AttrEnv()
        {
        }

        public virtual void Clear()
        {
            random = null;
            firstEffect?.Effect(first.GetAttr(), -1);
            secondEffect?.Effect(second.GetAttr(), -1);
            firstEffect = null;
            secondEffect = null;
            first = null;
            second = null;
            Hold();
            Release();
        }

        public static AttrEnv Init(int time, AttrObject first, AttrObject second)
        {
            return Init(1, time, first, second);
        }

        public static AttrEnv Init(float power, int time, AttrObject first, AttrObject second)
        {
            AttrEnv attrEnv = null;
            if (queue.TryDequeue(out attrEnv))
            {
            }

            if (attrEnv == null) attrEnv = new AttrEnv();
            attrEnv.Bind(power, time, first, second);
            return attrEnv;
        }

        protected void Bind(float power, int time, AttrObject first, AttrObject second)
        {
            this.time = time;
            this.power = power;
            random = new Random(time);
            BindObject(first, second);
            result = true;
        }

        public void EffectFirst(AttrEffectList list)
        {
            firstEffect = list;
            firstEffect?.Effect(first.GetAttr());
        }

        public void EffectSecond(AttrEffectList list)
        {
            secondEffect = list;
            secondEffect?.Effect(second.GetAttr());
        }

        public void RestoreEffect()
        {
            firstEffect?.Effect(first.GetAttr(), -1);
            secondEffect?.Effect(second.GetAttr(), -1);
        }

        private void BindObject(AttrObject f, AttrObject s)
        {
            if (f != null) first = f;

            if (s != null) second = s;
        }

        public void Hold()
        {
            result = true;
            co?.Clear();
        }

        public virtual void Release()
        {
            queue.Enqueue(this);
        }
    }

    public interface ActionProcess
    {
        bool PreEnv(AttrEventInfo info, int[] vars, AttrEnv attrEnv);
        bool PreLogic(AttrEventInfo info, AttrLogic logic, int[] vars, AttrEnv attrEnv);
        bool Process(AttrEventInfo info, AttrLogic logic, int[] vars, AttrEnv attrEnv);
    }

    public interface AttrComputeInterface
    {
        int ParseComputeTarget(AttrCompute compute, int[] vars, int[] logicParam, int[] runtimeParam, AttrEnv attrEnv);
    }

    public class AttrObjectPool
    {
        internal static ConcurrentQueue<int[]> queue1 = new();
        internal static ConcurrentQueue<int[]> queue2 = new();
        internal static ConcurrentQueue<int[]> queue3 = new();
        internal static ConcurrentQueue<int[]> queue4 = new();
        internal static ConcurrentQueue<int[]> queue5 = new();
        internal static ConcurrentQueue<int[]> queue6 = new();
        internal static ConcurrentQueue<int[]> queue7 = new();
        internal static ConcurrentQueue<int[]> queue8 = new();
        internal static ConcurrentQueue<int[]> queue9 = new();

        public static int[] Get(int length)
        {
            int[] v;
            switch (length)
            {
                case 1:
                    if (queue1.TryDequeue(out v))
                        if (v != null)
                            return v;

                    break;
                case 2:
                    if (queue2.TryDequeue(out v))
                        if (v != null)
                            return v;

                    break;
                case 3:
                    if (queue3.TryDequeue(out v))
                        if (v != null)
                            return v;

                    break;
                case 4:
                    if (queue4.TryDequeue(out v))
                        if (v != null)
                            return v;

                    break;
                case 5:
                    if (queue5.TryDequeue(out v))
                        if (v != null)
                            return v;

                    break;
                case 6:
                    if (queue6.TryDequeue(out v))
                        if (v != null)
                            return v;

                    break;
                case 7:
                    if (queue7.TryDequeue(out v))
                        if (v != null)
                            return v;

                    break;
                case 8:
                    if (queue8.TryDequeue(out v))
                        if (v != null)
                            return v;

                    break;
                case 9:
                    if (queue9.TryDequeue(out v))
                        if (v != null)
                            return v;

                    break;
            }

            var m = new int[length];
            return m;
        }

        public static void Put(int[] objects)
        {
            switch (objects.Length)
            {
                case 1:
                    queue1.Enqueue(objects);
                    break;
                case 2:
                    queue2.Enqueue(objects);
                    break;
                case 3:
                    queue3.Enqueue(objects);
                    break;
                case 4:
                    queue4.Enqueue(objects);
                    break;
                case 5:
                    queue5.Enqueue(objects);
                    break;
                case 6:
                    queue6.Enqueue(objects);
                    break;
                case 7:
                    queue7.Enqueue(objects);
                    break;
                case 8:
                    queue8.Enqueue(objects);
                    break;
                case 9:
                    queue9.Enqueue(objects);
                    break;
            }
        }
    }

    public class AttrEventInfo
    {
        public static AttrEventInfo DefaultEffect = new();
        public int category;

        public int type;
        public int value;

        public AttrEventInfo(int type, int category, int value)
        {
            this.type = type;
            this.category = category;
            this.value = value;
        }

        public AttrEventInfo(int type, int category)
        {
            this.type = type;
            this.category = category;
        }

        public AttrEventInfo(int type)
        {
            this.type = type;
        }

        public AttrEventInfo()
        {
        }
    }

    public class AttrManager : AttrDataManager
    {
        protected List<ActionProcess> _ActionList = new();

        protected ActionProcess[] ActionList;
        protected Dictionary<int, byte> AttrBattleIdIndexMap = new();
        protected List<int> AttrIdIndex = new();
        protected Dictionary<int, int> AttrIdIndexMap = new();
        protected List<string> AttrKeyIndex = new();
        protected Dictionary<string, int> AttrkeyIndexMap = new();
        protected Dictionary<int, string> AttrIdKeyMap = new();
        protected AttrInfo[] AttrList;
        protected int[] BattleIndexList;
        private Dictionary<int, byte> BattleIndexMap;

        protected string[] BattleList;
        public Dictionary<int, Buff> BuffMap = new();
        private int[] DataIndexList;
        private Dictionary<int, byte> DataIndexMap;

        private string[] DataList;
        protected List<int> GroupIdIndex = new();

        protected AttrGroup[] GroupList;

        public Dictionary<int, Group> GroupMap = new();
        public Dictionary<int, Skill> SkillMap = new();


        public int AttrIndex(int id)
        {
            return AttrIdIndexMap[id];
        }

        public int AttrIndex(string key)
        {
            return AttrkeyIndexMap[key];
        }

        public string AttrKey(int id)
        {
            return AttrIdKeyMap[id];
        }

        public int AttrLength()
        {
            return AttrList.Length;
        }

        public byte DataIndex(int id)
        {
            var attrIndex = AttrIndex(id);
            byte index;
            DataIndexMap.TryGetValue(attrIndex, out index);
            return index;
        }

        public byte DataIndex(string key)
        {
            var attrIndex = AttrIndex(key);
            byte index;
            DataIndexMap.TryGetValue(attrIndex, out index);
            return index;
        }

        public int DataLength()
        {
            return DataIndexList.Length;
        }

        public string[] DataKeys()
        {
            return DataList;
        }

        public int[] DataIndexs()
        {
            return DataIndexList;
        }

        public int AttrId(int index)
        {
            return AttrIdIndex[index];
        }

        public AttrInfo GetAttr(int index)
        {
            if (AttrList.Length > index && index >= 0) return AttrList[index];

            return null;
        }

        public void SetAttrs(object[] arr)
        {
            var attrList = new AttrInfo[arr.Length];
            var attrIdIndex = new List<int>();
            var attrIdIndexMap = new Dictionary<int, int>();
            var attrKeyIndex = new List<string>();
            var attrkeyIndexMap = new Dictionary<string, int>();
            var attrIdKeyMap = new Dictionary<int, string>();
            for (var i = 0; i < arr.Length; i++)
            {
                attrList[i] = new AttrInfo((object[])arr[i]);
                if (attrList[i].refInfo.key == "") attrList[i].refInfo.key = attrList[i].refInfo.id.ToString();
                attrIdIndex.Insert(i, attrList[i].refInfo.id);
                attrIdIndexMap.Add(attrList[i].refInfo.id, i);
                attrKeyIndex.Insert(i, attrList[i].refInfo.key);
                attrkeyIndexMap.Add(attrList[i].refInfo.key, i);
                attrIdKeyMap.Add(attrList[i].refInfo.id, attrList[i].refInfo.key);
                // LogTools.Data("Load", "Attr", "id->" + attrList[i].refInfo.id + " key->" + attrList[i].refInfo.key);
            }

            AttrList = attrList;
            AttrIdIndex = attrIdIndex;
            AttrIdIndexMap = attrIdIndexMap;
            AttrKeyIndex = attrKeyIndex;
            AttrkeyIndexMap = attrkeyIndexMap;
            AttrIdKeyMap = attrIdKeyMap;

            var dataIndexMap = new Dictionary<int, byte>();
            for (var i = 0; i < arr.Length; i++)
            {
                // Debug.LogError("attr:"+attrList[i].refInfo.key+"->"+attrList[i].isData);
                if (attrList[i].isData)
                {
                    // attrIndex=>dataIndex
                    dataIndexMap.Add(i, Convert.ToByte(dataIndexMap.Count));
                }
            }
                
            DataIndexMap = dataIndexMap;

            var dataIndexs = new int[dataIndexMap.Count];
            var dataList = new string[dataIndexMap.Count];
            var e = dataIndexMap.GetEnumerator();
            while (e.MoveNext())
            {
                // dataIndex => attrIndex
                dataIndexs[e.Current.Value] = e.Current.Key;
                // dataKey list
                dataList[e.Current.Value] = attrList[e.Current.Key].refInfo.key;
            }

            e.Dispose();
            DataIndexList = dataIndexs;
            DataList = dataList;

            var battleIndexMap = new Dictionary<int, byte>();
            var attrBattleIdIndexMap = new Dictionary<int, byte>();
            for (var i = 0; i < arr.Length; i++)
                if (attrList[i].isBattle)
                {
                    // Debug.LogError("attr d :"+attrList[i].refInfo.id+"->"+attrList[i].isBattle+" "+i+"=>"+Convert.ToByte(battleIndexMap.Count));
                    attrBattleIdIndexMap.Add(attrList[i].refInfo.id, Convert.ToByte(battleIndexMap.Count));
                    battleIndexMap.Add(i, Convert.ToByte(battleIndexMap.Count));
                }

            BattleIndexMap = battleIndexMap;
            AttrBattleIdIndexMap = attrBattleIdIndexMap;

            var battleIndexs = new int[battleIndexMap.Count];
            var battleList = new string[battleIndexMap.Count];
            var ee = battleIndexMap.GetEnumerator();
            while (ee.MoveNext())
            {
                // dataIndex => attrIndex
                battleIndexs[ee.Current.Value] = ee.Current.Key;
                // dataKey list
                battleList[ee.Current.Value] = attrList[ee.Current.Key].refInfo.key;
            }

            ee.Dispose();
            BattleIndexList = battleIndexs;
            BattleList = battleList;
        }

        public void SetGroups(object[] arr)
        {
            var groupMap = new Dictionary<int, Group>();
            if (arr != null)
            {
                foreach (object[] obj in arr) groupMap.Add(Convert.ToInt32(obj[(byte)GROUP.ID]), new Group(obj));
            }
            GroupMap = groupMap;
        }

        public void SetSkills(object[] arr)
        {
            var skillMap = new Dictionary<int, Skill>();
            foreach (object[] obj in arr)
            {
                skillMap.Add(Convert.ToInt32(obj[(byte)SKILL.ID]), new Skill(obj));
                // LogTools.Data("Load", "Skill", new Skill(obj).ToString());
            }

            SkillMap = skillMap;
        }

        public void SetSkills(Skill[] arr)
        {
            var skillMap = new Dictionary<int, Skill>();
            foreach (Skill obj in arr)
            {
                skillMap.Add(obj.refInfo.id, obj);
                // LogTools.Data("Load", "Skill", obj.ToString());
            }

            SkillMap = skillMap;
        }

        public void SetBuffs(object[] arr)
        {
            var buffMap = new Dictionary<int, Buff>();
            foreach (object[] obj in arr)
            {
                buffMap.Add(Convert.ToInt32(obj[(byte)BUFF.ID]), new Buff(obj, this));
                // LogTools.Data("Load", "Buff", new Buff(obj, this).ToString());
            }

            BuffMap = buffMap;
        }


        public Group GetGroup(int id)
        {
            if (GroupMap.ContainsKey(id)) return GroupMap[id];
            return null;
        }

        public Skill GetSkill(int id)
        {
            if (SkillMap.ContainsKey(id)) return SkillMap[id];
            return null;
        }

        public Buff GetBuff(int id)
        {
            if (BuffMap.ContainsKey(id)) return BuffMap[id];
            return null;
        }

        public int BattleIndex(int id)
        {
            byte index;
            var ok = AttrBattleIdIndexMap.TryGetValue(id, out index);
            if (!ok) return -1;
            return index;
        }

        public int BattleIndex(string key)
        {
            var attrIndex = AttrIndex(key);
            byte index;
            var ok = BattleIndexMap.TryGetValue(attrIndex, out index);
            if (!ok) return -1;
            return index;
        }

        public int BattleFromIndex(int attrIndex)
        {
            byte index;
            var ok = BattleIndexMap.TryGetValue(attrIndex, out index);
            if (!ok) return -1;
            return index;
        }

        public int AttrIdFromBattle(int index)
        {
            return AttrId(BattleIndexList[index]);
        }

        public int BattleLength()
        {
            return BattleIndexList.Length;
        }

        public string[] BattleKeys()
        {
            return BattleList;
        }

        public int[] BattleIndexs()
        {
            return BattleIndexList;
        }

        public AttrGroup GetAttrGroup(int index)
        {
            if (GroupList.Length > index && index >= 0) return GroupList[index];

            return null;
        }

        public int GroupIndex(int id)
        {
            return GroupIdIndex.IndexOf(id);
        }

        public int GroupId(int index)
        {
            return GroupIdIndex[index];
        }

        public int GroupLength()
        {
            return GroupList.Length;
        }

        public void SetAttrGroups(object[] arr)
        {
            var groupList = new AttrGroup[arr.Length];
            var groupIdIndex = new List<int>();
            for (var i = 0; i < arr.Length; i++)
            {
                groupList[i] = new AttrGroup((object[])arr[i]);
                groupIdIndex.Insert(i, groupList[i].refInfo.id);
                // LogTools.Data("Load", "AttrGroup", groupList[i].ToString());
            }

            GroupList = groupList;
            GroupIdIndex = groupIdIndex;
        }

        public void RegisterActions(ActionProcess process)
        {
            _ActionList.Add(process);
        }

        public int ActionLength()
        {
            return ActionList.Length;
        }

        public ActionProcess GetAction(int index)
        {
            return ActionList[index];
        }

        public void Start()
        {
            ActionList = new ActionProcess[_ActionList.Count];
            for (var i = 0; i < _ActionList.Count; i++) ActionList[i] = _ActionList[i];
            _ActionList.Clear();
        }
    }

    public class AttrTools
    {
        private readonly AttrComputeInterface Interface;
        private readonly AttrManager Manager;

        public AttrTools(AttrManager manager, AttrComputeInterface inf)
        {
            Manager = manager;
            Interface = inf;
        }

        public bool Judge(AttrConditionList conditionList, int[] vars, AttrEnv attrEnv)
        {
            if (conditionList == null) return true;
            foreach (var condition in conditionList.attrConditions)
            {
                AttrInfo attrInfo;
                var index = -1;
                var value = 0;
                switch (condition.target)
                {
                    case AttrConditionTarget.First:
                        index = Manager.AttrIndex(condition.index);
                        attrInfo = Manager.GetAttr(index);
                        value = attrInfo.Compute(attrEnv.first.GetAttr().GetWithIndex(index), attrEnv.random);
                        break;
                    case AttrConditionTarget.Second:
                        index = Manager.AttrIndex(condition.index);
                        attrInfo = Manager.GetAttr(index);
                        // Debug.Log(attrEnv.second.GetAttr());
                        // Debug.Log(attrInfo);
                        value = attrInfo.Compute(attrEnv.second.GetAttr().GetWithIndex(index), attrEnv.random);
                        break;
                    case AttrConditionTarget.Var:
                        value = vars[condition.index];
                        break;
                    case AttrConditionTarget.VarPercent:
                        value = UnityTools.IsOk(attrEnv.random, vars[condition.index]) ? 1 : 0;
                        break;
                    default:
                        return false;
                }

                if (!condition.Compute(vars, value)) return false;
            }

            return true;
        }

        public void Compute(AttrComputeList computeList, int[] vars, int[] logicParam, int[] runtimeParam,
            AttrEnv attrEnv)
        {
            if (computeList == null) return;
            foreach (var compute in computeList.attrComputes)
            {
                // Debug.LogError("compute:"+compute.index+"="+vars[compute.index]);
                vars[compute.index] = compute.Compute(vars[compute.index],
                    Interface.ParseComputeTarget(compute, vars, logicParam, runtimeParam, attrEnv), attrEnv.random);
                // Debug.LogError("compute end:"+compute.index+"="+vars[compute.index]);
            }
                
        }

        public void AdditionHandle(AttrEventInfo info, AttrEvent attrEvent, AttrEnv attrEnv)
        {
            if (attrEvent == null || attrEvent.list == null) return;
            // Debug.LogError($"addition: {attrEvent.list.attrAdditions}");
            foreach (var attrAddition in attrEvent.list.attrAdditions)
            {
                var group = Manager.GetAttrGroup(Manager.GroupIndex(attrAddition.groupIndex));
                // Debug.LogError($"addition group: {group}");
                if (group == null) continue;
                GroupHandle(info, group, attrAddition.param, attrEnv);
            }
        }

        public void GroupHandle(AttrEventInfo info, AttrGroup group, int[] param, AttrEnv attrEnv)
        {
            if (group.logicList == null) return;
            if (group.type >= Manager.ActionLength()) return;
            var process = Manager.GetAction(group.type);
            var vars = AttrObjectPool.Get(group.varLength);
            for (var i = 0; i < vars.Length; i++) vars[i] = 0;
            if (process.PreEnv(info, vars, attrEnv))
            {
                for (var i = 0; i < group.logicList.attrLogics.Length; i++)
                {
                    var logic = group.logicList.attrLogics[i];
                    if (logic == null) continue;
                    if (!process.PreLogic(info, logic, vars, attrEnv)) continue;
                    // Debug.LogError($"handle ${i} ${vars[0]}, ${vars[1]}, ${vars[2]}");
                    if (!Judge(logic.conditionList, vars, attrEnv)) continue;
                    Compute(logic.computeList, vars, logic.param, param, attrEnv);
                    //  if (vars.Length > 6)
                    // {
                    //   Debug.LogError($"handle compute ${i} ${vars[0]}, ${vars[1]}, ${vars[2]}, ${vars[3]}, ${vars[4]}, ${vars[5]}");
                    // }
                    if (process.Process(info, logic, vars, attrEnv)) i -= 1;
                }

                if (attrEnv.co != null)
                {
                    attrEnv.co.Clear();
                    attrEnv.co = null;
                }
            }

            AttrObjectPool.Put(vars);
        }
    }
}